﻿using System;
using System.Collections.Generic;
using System.Linq;

namespace KxParser.Parser.LR0
{
    class ParserTable
    {
        private readonly State[] _states;
        private object[,] _table;// = new string[row, column];

        public ParserTable(State[] states)
        {
            _states = states;

            InitParserTable();

            FillParserTable();
        }

        private void InitParserTable()
        {
            _table = new object[_states.Length + 1, 1 + Global.Terminals.Length + Global.NonTerminals.Length];

            _table[0, 0] = "S_ID";

            var colIndex = 1;
            foreach (var terminal in Global.Terminals)
            {
                _table[0, colIndex++] = terminal;
            }

            colIndex = 1 + Global.Terminals.Length;
            foreach (var nonTerminal in Global.NonTerminals)
            {
                _table[0, colIndex++] = nonTerminal;
            }

            var rowIndex = 1;
            foreach (var state in _states)
            {
                _table[rowIndex++, 0] = state.Id.ToString();
            }
        }

        private void FillParserTable()
        {
            var tblActions = new List<TableAction>();

            foreach (var state in _states)
            {
                foreach (var productionItem in state.GetProductionItems())
                {
                    var action = GetAction(productionItem);

                    if(state.Id == 4 && productionItem.OverItem=="+")
                    {
                        var i = 1; // stop here
                    }

                    //if (action.Action == TableAction.EAction.Reduce)
                    //{
                    //    for (var i = 1; i <= Global.Terminals.Length; i++)
                    //    {
                    //        //if (_table[GetRow(state.Id), i] == null)
                    //        //    _table[GetRow(state.Id), i] = new TableActionz();
                    //        //((TableActionz)_table[GetRow(state.Id), i]).Add(action);
                    //        _table[GetRow(state.Id), i] = action;
                    //    }
                    //}

                    //if (_table[GetRow(state.Id), GetCol(productionItem.OverItem)] == null)
                    //    _table[GetRow(state.Id), GetCol(productionItem.OverItem)] = new TableActionz();
                    //((TableActionz)_table[GetRow(state.Id), GetCol(productionItem.OverItem)]).Add(action);
                    _table[GetRow(state.Id), GetCol(productionItem.OverItem)] = action;
                }
            }
        }

        private TableAction GetAction(ProductionItem productionItem)
        {
            //var ta = new List<TableAction>();

            if (productionItem.IsEnder)
            {
                return new TableAction{Action = TableAction.EAction.Accept};
            }

            if (productionItem.CurrentState.Id > 1 && productionItem.ContainsRule != -1 /*&& productionItem.CurrentState.GetProductionItems().Length==1*/)
            {
                return new TableAction { Action = TableAction.EAction.Reduce, Data = productionItem.ContainsRule};
            }

            if (productionItem.NextState == null)
            {
                return new TableAction{Action = TableAction.EAction.Error};
            }

            if (Global.Terminals.Contains(productionItem.OverItem))
            {
                return new TableAction {Action = TableAction.EAction.Shift, Data = productionItem.NextState.Id};
            }

            if (Global.NonTerminals.Contains(productionItem.OverItem))
            {
                return new TableAction {Action = TableAction.EAction.Goto, Data = productionItem.NextState.Id};
            }

            return new TableAction {Action = TableAction.EAction.Error};
        }

        public TableAction GetAction(int stateId, string terminal)
        {
            var o = _table[GetRow(stateId), GetCol(terminal)];
            if (o == null)
                return null;

            if(typeof(TableAction).IsAssignableFrom(o.GetType()))
                return (TableAction)o;
            return null;
        }

        int GetRow(int stateId)
        {
            for(int i=0; i<_states.Length; i++)
            {
                if(_states[i].Id == stateId)
                {
                    return i + 1;
                }
            }
            return 0;
        }

        int GetCol(string columnName)
        {
            for (int i = 1; i < _table.GetLength(1); i++)
            {
                if (_table[0, i].Equals(columnName))
                {
                    return i;
                }
            }
            return 0;
        }

        public override string ToString()
        {
            var s = Environment.NewLine;

            for(int row = 0; row < _table.GetLength(0); ++row)
            {
                s += Environment.NewLine;
                for(int col = 0; col < _table.GetLength(1); col++)
                {
                    if (_table[row, col] != null && typeof(TableActionz).IsAssignableFrom(_table[row, col].GetType()))
                    {
                        var l = (TableActionz) _table[row, col];
                        string c = null;
                        foreach (var tableAction in l.GetActions())
                        {
                            if (!string.IsNullOrEmpty(c))
                                c += " ";
                            c += tableAction;
                        }
                        s += string.Format("{0,-5}", c);
                    }
                    else
                        s += string.Format("{0,-5}", _table[row, col]);
                }
            }

            return s;
        }
    }

    internal class TableActionz
    {
        private List<TableAction> actionzList = new List<TableAction>();

        public void Add(TableAction tableAction)
        {
            actionzList.Add(tableAction);
        }

        public TableAction[] GetActions()
        {
            return actionzList.ToArray();
        }
    }
}
